package com.share.order.listener;

import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import com.share.common.rabbit.constant.MqConst;
import com.share.common.rabbit.service.RabbitService;
import com.share.order.domain.EndOrderVo;
import com.share.order.domain.SubmitOrderVo;
import com.share.order.service.IOrderInfoService;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 接收Rabbit MQ中消息，处理订单相关操作
 */
@Slf4j
@Component
public class OrderListener {

    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IOrderInfoService orderInfoService;

    //租借 弹出充电宝 创建订单
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_ORDER,durable = "true"),
            value = @Queue(value = MqConst.QUEUE_SUBMIT_ORDER,durable = "true"),
            key = MqConst.ROUTING_SUBMIT_ORDER
    ))
    public void createOrder(String content, Message message, Channel channel) throws IOException {
        log.info("[订单服务]租借充电宝消息：{}", content);

        //将消息转换为Vo类型
        SubmitOrderVo submitOrderVo = JSONObject.parseObject(content, SubmitOrderVo.class);

        //利用Redis setnx 实现业务幂等性 保证消息不会被重复处理（不会重复创建订单）
        String messageNo = submitOrderVo.getMessageNo(); //消息编号

        String key = "order:submit:" + messageNo;

        //key：消息编号 value:消息内容 过期时间1h
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, content, 1, TimeUnit.HOURS);//setnx

        if (!isExist){
            //返回false 说明redis执行命令失败 消息已存在 直接返回即可
            log.info("重复请求: {}", content);
            return;
        }

        //调用订单服务创建订单
        try {
            orderInfoService.createOrder(submitOrderVo);
            //成功处理消息创建订单后，需要手动处理确认消息 返回ACK  参数3：仅确认/拒绝当前单条消息。
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

        } catch (Exception e) {
            //没有正确处理消息创建订单
            log.error("订单服务：创建订单失败，订单编号：{}", messageNo, e);
            redisTemplate.delete(key);//同步将redis数据删除
            // 消费处理异常，执行消息重发，返回nack  参数3同上  参数4：控制消息是否重新放回队列。
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }

    //插入充电宝 结束订单
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_ORDER,durable = "true"),
            value = @Queue(value = MqConst.QUEUE_END_ORDER,durable = "true"),
            key = MqConst.ROUTING_END_ORDER
    ))
    public void endOrder(String content, Message message, Channel channel ) throws IOException {
        log.info("[订单服务]归还充电宝消息：{}", content);

        //将json字符串消息转换为对象
        EndOrderVo endOrderVo = JSONObject.parseObject(content, EndOrderVo.class);
        String messageNo = endOrderVo.getMessageNo();
        //分布式锁 防止重复处理
        String key = "order:endOrder:" + messageNo;

        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, content, 1, TimeUnit.HOURS);

        if (!isExist){
            log.info("重复消息: {}", content);
            return;
        }

        //调用order模块结束订单
        try {
            orderInfoService.endOrder(endOrderVo);

            //成功处理消息创建订单后，需要手动处理确认消息 返回ACK  参数3：仅确认/拒绝当前单条消息。
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            //没有正确处理消息创建订单
            log.error("订单服务：创建订单失败，订单编号：{}", messageNo, e);
            redisTemplate.delete(key);//同步将redis数据删除
            // 消费处理异常，执行消息重发，返回nack  参数3同上  参数4：控制消息是否重新放回队列。
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }

    }

}
